package com.example.spider.config.proxy;

import com.example.spider.proxy.core.ProxyInfo;
import com.example.spider.proxy.core.ProxyRepository;
import com.example.spider.proxy.core.ProxyTester;
import com.example.spider.proxy.core.distinct.FileBasedIpPortDistincter;
import com.example.spider.proxy.core.distinct.IpPortDistincter;
import com.example.spider.proxy.core.distinct.MemoryIpPortDistincter;
import com.example.spider.proxy.core.distinct.RedisIpPortDistincter;
import com.example.spider.proxy.core.repository.FileBasedMemoryProxyRepository;
import com.example.spider.proxy.core.repository.RedisProxyRepository;
import com.example.spider.proxy.core.tester.*;
import com.example.spider.util.FileBasedCollection;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.shoulder.autoconfigure.condition.ConditionalOnCluster;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.File;
import java.util.concurrent.ConcurrentSkipListSet;

@Configuration
public class ProxyConfiguration {

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnCluster(cluster = false)
    @ConditionalOnProperty(name = "spider.proxy.distincter.type", havingValue = "memory", matchIfMissing = true)
    public IpPortDistincter memoryIpPortDistincter() {
        return new MemoryIpPortDistincter();
    }

    @Bean
    @ConditionalOnCluster
    @ConditionalOnProperty(name = "spider.proxy.distincter.type", havingValue = "redis", matchIfMissing = true)
    public IpPortDistincter redisIpPortDistincter(RedissonClient redissonClient) {
        return new RedisIpPortDistincter(redissonClient);
    }

    @Configuration
    @ConditionalOnProperty(name = "spider.proxy.distincter.type", havingValue = "file", matchIfMissing = false)
    public static class fileBasedIpPortDistincterConfiguration {

        @Bean
        public FileBasedIpPortDistincter fileBasedIpPortDistincter(@Value("${spider.proxy.storage.distinctSet.file:${user.home}/spider/ipProxy/allIpPort.txt}") String filePath) {

            return new FileBasedIpPortDistincter(
                    new FileBasedCollection<>(new ConcurrentSkipListSet<>(), new File(filePath))
            );
        }

    }

    // ------------------------------------------------------------

    @Bean(ProxyTester.BASE_TESTER)
    public ProxyTester baseProxyTester(UsableTestCallBack usableTestCallBack) {
        return new SimpleProxyTester(usableTestCallBack);
    }

    @Bean(ProxyTester.FILL_INFO_TESTER)
    public ProxyTester fillInfoProxyTester(UpdateLocationTestCallBack updateLocationTestCallBack) {
        return new FillInfoProxyTester(updateLocationTestCallBack);
    }

    @Bean
    public UpdateLocationTestCallBack updateLocationTestCallBack() {
        return new UpdateLocationTestCallBack();
    }

    @Bean
    public UsableTestCallBack usableTestCallBack() {
        return new UsableTestCallBack(TestConstant.DEFAULT_TEST_URL);
    }

    //    @Bean(ProxyTester.BIZ_TESTER)
//    public ProxyTester bizProxyTester() {
//        return new SimpleProxyTester(new UsableTestCallBack(TestConstant.DEFAULT_TEST_URL));
//    }

    // ------------------------------------------------------------

    @Bean(ProxyRepository.SOURCE)
    @ConditionalOnProperty(name = "spider.proxy.storage.source.type", havingValue = "file", matchIfMissing = true)
    public ProxyRepository fileBasedMemorySourceProxyRepository(@Value("${spider.proxy.storage.source.file}") String storageFile) {
        return new FileBasedMemoryProxyRepository(storageFile);
    }

    @Bean(ProxyRepository.USABLE)
    @ConditionalOnProperty(name = "spider.proxy.storage.usable.type", havingValue = "file", matchIfMissing = true)
    public ProxyRepository fileBasedMemoryUsableProxyRepository(@Value("${spider.proxy.storage.usable.file}") String storageFile) {
        return new FileBasedMemoryProxyRepository(storageFile);
    }

    @Bean(ProxyRepository.STABLE)
    @ConditionalOnProperty(name = "spider.proxy.storage.stable.type", havingValue = "file", matchIfMissing = true)
    public ProxyRepository fileBasedMemoryStableProxyRepository(@Value("${spider.proxy.storage.stable.file}") String storageFile) {
        return new FileBasedMemoryProxyRepository(storageFile);
    }

    // ------------------------------------------------------------

    @Bean(ProxyRepository.SOURCE)
    @ConditionalOnProperty(name = "spider.proxy.storage.source.type", havingValue = "redis", matchIfMissing = false)
    public ProxyRepository redisSourceProxyRepository(RedissonClient redissonClient) {
        RMap<String, ProxyInfo> map = redissonClient.getMap(ProxyRepository.SOURCE_REPO_REDIS_KEY_DEFAULT);
        return new RedisProxyRepository(map);
    }

    @Bean(ProxyRepository.USABLE)
    @ConditionalOnProperty(name = "spider.proxy.storage.usable.type", havingValue = "redis", matchIfMissing = false)
    public ProxyRepository redisUsableProxyRepository(RedissonClient redissonClient) {
        RMap<String, ProxyInfo> map = redissonClient.getMap(ProxyRepository.USABLE_REPO_REDIS_KEY_DEFAULT);
        return new RedisProxyRepository(map);
    }

    @Bean(ProxyRepository.STABLE)
    @ConditionalOnProperty(name = "spider.proxy.storage.stable.type", havingValue = "redis", matchIfMissing = false)
    public ProxyRepository redisStableProxyRepository(RedissonClient redissonClient) {
        RMap<String, ProxyInfo> map = redissonClient.getMap(ProxyRepository.STABLE_REPO_REDIS_KEY_DEFAULT);
        return new RedisProxyRepository(map);
    }

}